/////////////////////////////////////////////////////////////
//                      C++ Serialization                  //
//                                                         //
//  copyright:   (c) 2011 CPPS                             //
//  authors:     Hikki-Kun                                 //
//  mail:        glrenderable@gmail.com                    //
//                                                         //
//  This program is made available under the terms of      //
//  the MIT License.                                       //
/////////////////////////////////////////////////////////////
#ifndef XML_SERIALIZATOR_HPP
#define XML_SERIALIZATOR_HPP

#include "ISerialization.hpp"
#include "Dispatcher.hpp"

#include <ostream>
#include <string>
#include <vector>

namespace Serialization
{
  template <typename T>
  class XMLSerializer : public ISerializer<T> {
    //
    //  to be able to call "XMLSerializer.Dispatch"
    //
    friend Dispatcher<XMLSerializer<T>>;

    //
    //  private data members
    //
  private:  
    //
    //  m_stream holds output stream
    //
    std::ostream  &m_stream;
    //
    //  m_shift represents current 
    //  right side shift
    //
    size_t m_shift;

    //
    //  Hide the default copy constructor
    //
  private:
    //XMLSerializer(const XMLSerializer &inst) { }
  
    //
    //  XML Formatters
    //  
  private:
    ////////////// DEBUG! UNSAFE! SLOW! //////////////
    void Indent(const size_t &additional = 0) {
      for (size_t i = 0; i < m_shift + additional; i++)
        m_stream << ' ';
    }

    void OpenAtomTag(const std::string &name) {
      Indent();
      m_stream << '<'  << name << '>';
    }

    void OpenComplexTag(const std::string &name) {
      OpenAtomTag(name);
      m_stream << std::endl;
      m_shift += 2;        
    }

    void CloseComplexTag(const std::string &name) {
      m_shift -= 2;  
      Indent();
      CloseAtomTag(name);
    }

    void CloseAtomTag(const std::string &name) {
      m_stream << "</" << name << '>' << std::endl;
    }

    //
    //  Generic XML writer invoked by dispatch
    // 
  private:
    template <typename T>
    void WriteAtomTag(const T &value, const std::string &name) {
      OpenAtomTag(name);
      m_stream << value;
      CloseAtomTag(name);
    }
    
    //
    //  Serialization of a complex type leads to 
    //  instantiation of the nested own template 
    //
  private:
    template <typename T>
    void Serializate(const T &value, const std::string &name) {
      OpenComplexTag(name);

      XMLSerializer<T> ser(m_stream, m_shift);
      value.__SERIALIZE__META__(ser);
     
      CloseComplexTag(name);
    }

    //
    //  Serialization of vector
    //
  private:
    template <typename T>
    void Serializate(const std::vector<T> &vec, const std::string &name) {
      OpenComplexTag(name);

      Serializate(int(vec.size()), "CollectionCount");
      for (std::vector<T>::const_iterator i = vec.begin(); i < vec.end(); ++i)
        Serializate(*i, "Item");
      
      CloseComplexTag(name);
    }
   
    //
    //  Serialization of atom types
    //
  private:
    template <>
    void Serializate(const int &value, const std::string &name) {
      WriteAtomTag(value, name);
    }
    
    template <>
    void Serializate(const std::string &value, const std::string &name) {
      WriteAtomTag(value, name);
    }

    template <>
    void Serializate(const float &value, const std::string &name) {
      WriteAtomTag(value, name);
    }

    template <>
    void Serializate(const double &value, const std::string &name) {
      WriteAtomTag(value, name);
    }

    //
    //  Used by Dispatcher for flushing arguments
    //
    template <typename T>
    void Dispatch(const T &value, const std::string &name) {
      Serializate(value, name);
    }

    //
    //  public constructors
    //
  public:
    XMLSerializer(std::ostream &stream, size_t initialShift = 0) : 
        m_stream(stream), 
        m_shift(initialShift) { 

    }

    //
    //  ISerializator interface implemantation
    //
  public:
    const XMLSerializer& operator<<(const T &value) {
      value.__SERIALIZE__META__(*this);
      return *this;
    }
  };
}

#endif